package com.info.zhiduoduo.shiro.configs;

import com.info.zhiduoduo.shiro.AuthRealm;
import com.info.zhiduoduo.shiro.MySessionManager;
import com.info.zhiduoduo.shiro.filter.ProcessOptionsFilter;
import com.info.zhiduoduo.shiro.filter.URLPathMatchingFilter;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;

import java.util.LinkedHashMap;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.Filter;

// @Configuration
// @EnableConfigurationProperties(ShiroRedisConfig.class)
public class ShiroConfig {

	@Resource
	ShiroRedisConfig shiroRedisConfig;

	//********************************** 权限验证设置 **********************************//

	/**
	 * Filter工厂，设置对应的过滤条件和跳转条件
	 * create by: cuichenglong
	 *
	 * @return ShiroFilterFactoryBean
	 */
	@Bean
	public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {

		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
		//        shiroFilterFactoryBean.setLoginUrl("/login");
		// 过滤器链定义映射
		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
		//自定义拦截器
		Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
		//访问权限配置
		filtersMap.put("anon", new ProcessOptionsFilter());
		filtersMap.put("authc", getURLPathMatchingFilter());
		shiroFilterFactoryBean.setFilters(filtersMap);
		/*
		 * anon:所有url都都可以匿名访问，authc:所有url都必须认证通过才可以访问;
		 * 过滤链定义，从上向下顺序执行，authc 应放在 anon 下面
		 * */
		filterChainDefinitionMap.put("/sys/login", "anon");
		// 所有url都必须认证通过才可以访问
		filterChainDefinitionMap.put("/**", "authc");
		// 配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了, 位置放在 anon、authc下面
		//        filterChainDefinitionMap.put("/logout", "logout");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		return shiroFilterFactoryBean;
	}

	/**
	 * 访问权限拦截器
	 *
	 * @return
	 */
	public URLPathMatchingFilter getURLPathMatchingFilter() {
		return new URLPathMatchingFilter();
	}

	/**
	 * 凭证匹配器（由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了）
	 * create by: cuichenglong
	 *
	 * @return HashedCredentialsMatcher
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		// 散列算法:这里使用MD5算法;
		hashedCredentialsMatcher.setHashAlgorithmName("md5");
		// 散列的次数，比如散列两次，相当于 md5(md5(""));
		hashedCredentialsMatcher.setHashIterations(2);
		return hashedCredentialsMatcher;
	}

	/**
	 * 将自己的验证方式加入容器
	 * create by: cuichenglong
	 *
	 * @return MyShiroRealm
	 */
	@Bean
	public AuthRealm authRealm(HashedCredentialsMatcher hashedCredentialsMatcher) {
		AuthRealm authRealm = new AuthRealm();
		authRealm.setCredentialsMatcher(hashedCredentialsMatcher);
		return authRealm;
	}

	/**
	 * RedisSessionDAO shiro sessionDao层的实现 通过redis, 使用的是shiro-redis开源插件
	 * create by: cuichenglong
	 *
	 * @return RedisSessionDAO
	 */
	@Bean
	public RedisSessionDAO redisSessionDAO(JavaUuidSessionIdGenerator sessionIdGenerator) {
		RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
		redisSessionDAO.setRedisManager(redisManager());
		redisSessionDAO.setSessionIdGenerator(sessionIdGenerator);
		redisSessionDAO.setExpire(shiroRedisConfig.getSessionTimeout());
		return redisSessionDAO;
	}

	/**
	 * Session ID 生成器
	 * <br/>
	 * create by: cuichenglong
	 * <br/>
	 *
	 * @return JavaUuidSessionIdGenerator
	 */
	@Bean
	public JavaUuidSessionIdGenerator sessionIdGenerator() {
		return new JavaUuidSessionIdGenerator();
	}

	/**
	 * 自定义sessionManager
	 * create by: cuichenglong
	 *
	 * @return SessionManager
	 */
	@Bean
	public SessionManager sessionManager(RedisSessionDAO redisSessionDAO) {
		MySessionManager mySessionManager = new MySessionManager();
		mySessionManager.setSessionDAO(redisSessionDAO);
		return mySessionManager;
	}

	/**
	 * 配置shiro redisManager, 使用的是shiro-redis开源插件
	 * <br/>
	 * create by: cuichenglong
	 * <br/>
	 *
	 * @return RedisManager
	 */
	private RedisManager redisManager() {
		RedisManager redisManager = new RedisManager();
		redisManager.setHost(shiroRedisConfig.getHost());
		redisManager.setPort(shiroRedisConfig.getPort());
		redisManager.setTimeout(Integer.valueOf(shiroRedisConfig.getTimeout()));
		redisManager.setPassword(shiroRedisConfig.getPassword());
		return redisManager;
	}

	/**
	 * cacheManager 缓存 redis实现, 使用的是shiro-redis开源插件
	 * <br/>
	 * create by: cuichenglong
	 * <br/>
	 *
	 * @return RedisCacheManager
	 */
	@Bean
	public RedisCacheManager redisCacheManager() {
		RedisCacheManager redisCacheManager = new RedisCacheManager();
		redisCacheManager.setRedisManager(redisManager());
		// 必须要设置主键名称，shiro-redis 插件用过这个缓存用户信息
		redisCacheManager.setPrincipalIdFieldName(shiroRedisConfig.getPrincipalid());
		return redisCacheManager;
	}

	/**
	 * create by: cuichenglong
	 * description: 权限管理，配置主要是Realm的管理认证
	 *
	 * @return SecurityManager
	 */
	@Bean
	public SecurityManager securityManager(AuthRealm authRealm, SessionManager sessionManager, RedisCacheManager redisCacheManager) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(authRealm);
		// 自定义session管理 使用redis
		securityManager.setSessionManager(sessionManager);
		// 自定义缓存实现 使用redis
		securityManager.setCacheManager(redisCacheManager);
		return securityManager;
	}

	/*
	 * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
	 * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
	 */
	@Bean
	public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
		advisorAutoProxyCreator.setProxyTargetClass(true);
		return advisorAutoProxyCreator;
	}

	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}

	@Bean
	public SimpleCookie cookie() {
		// cookie的name,对应的默认是 JSESSIONID
		SimpleCookie cookie = new SimpleCookie("SHARE_JSESSIONID");
		cookie.setHttpOnly(true);
		//  path为 / 用于多个系统共享 JSESSIONID
		cookie.setPath("/");
		return cookie;
	}
}
